package com.zhouyu.proxy;

import com.zhouyu.service.UserInterface;
import com.zhouyu.service.UserService;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyDemo {
	public static void main(String[] args) {
//		userServiceCglibProxy();
//		userServiceJdkProxy();
//		userServiceProxyFactory();
//		advisorTest();
		proxyFactoryBeanTest();
	}

	public static void userServiceCglibProxy() {
		// 通过cglib技术
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(UserService.class);

		// 定义额外逻辑，也就是代理逻辑
		enhancer.setCallbacks(new Callback[]{
				new org.springframework.cglib.proxy.MethodInterceptor() {
					@Override
					public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
						System.out.println("Before method: " + method.getName());
						Object result = methodProxy.invokeSuper(o, objects);
						System.out.println("After method: " + method.getName());
						return result;
					}
				}
		});

		// 动态代理所创建出来的UserService对象
		UserService proxyUserService = (UserService) enhancer.create();
		// 执行这个userService的test方法时，就会额外会执行一些其他逻辑
		proxyUserService.test();
	}

	public static void userServiceJdkProxy() {
		UserService target = new UserService();

		// UserInterface接口的代理对象
		Object proxyObject = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserInterface.class}, (proxy, method, args) -> {
			System.out.println("Before method: " + method.getName());
			Object result = method.invoke(target, args);
			System.out.println("After method: " + method.getName());
			return result;
		});
		UserInterface proxyUserService = (UserInterface) proxyObject;
		proxyUserService.test();
	}

	public static void userServiceProxyFactory() {
		UserService target = new UserService();
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setTarget(target);
		proxyFactory.addAdvice(new MethodInterceptor() {
			@Nullable
			@Override
			public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
				System.out.println("Before method: " + invocation.getMethod().getName());
				Object result = invocation.proceed();
				System.out.println("After method: " + invocation.getMethod().getName());
				return result;
			}
		});
		UserService proxyUserService = (UserService) proxyFactory.getProxy();
		proxyUserService.test();
	}

	public static void advisorTest() {
		UserService target = new UserService();
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setTarget(target);
		proxyFactory.addAdvisor(new PointcutAdvisor() {
			@Override
			public Pointcut getPointcut() {
				return new StaticMethodMatcherPointcut() {
					@Override
					public boolean matches(Method method, Class<?> targetClass) {
						return method.getName().equals("test");
					}
				};
			}

			@Override
			public Advice getAdvice() {
				return new MethodInterceptor() {
					@Nullable
					@Override
					public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
						System.out.println("Before method: " + invocation.getMethod().getName());
						Object result = invocation.proceed();
						System.out.println("After method: " + invocation.getMethod().getName());
						return result;
					}
				};
			}

			@Override
			public boolean isPerInstance() {
				return false;
			}
		});
		UserService proxyUserService = (UserService) proxyFactory.getProxy();
		proxyUserService.test();
		proxyUserService.testUser();
	}

	public static void proxyFactoryBeanTest() {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProxyConfig.class);
		UserService userService = (UserService) context.getBean("userServiceProxy");
		userService.test();
		userService = (UserService) context.getBean("userServiceProxy2");
		userService.test();
	}
}
